package com.ehsunbehravesh.service.security.service;

import com.ehsunbehravesh.service.security.ejb.encryption.EncryptionLocalBusiness;
import com.ehsunbehravesh.service.security.service.jaxb.AESRequest;
import com.ehsunbehravesh.service.security.service.jaxb.BaseResult;
import com.ehsunbehravesh.service.security.service.jaxb.AESKeyResponse;
import com.ehsunbehravesh.service.security.service.jaxb.AESResponse;
import com.ehsunbehravesh.service.security.service.jaxb.RSAKeyResponse;
import com.ehsunbehravesh.service.security.service.jaxb.RSARequest;
import com.ehsunbehravesh.service.security.service.jaxb.RSAResponse;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.interfaces.RSAKey;
import javax.crypto.SecretKey;
import javax.ejb.EJB;
import javax.ejb.LocalBean;
import javax.ejb.Stateless;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

/**
 * The rest service for encryption operations.
 *
 * @author ehsun.behravesh
 */
@Stateless
@LocalBean
@Path("encryption")
public class EncryptionService {

  /**
   * EJB
   */
  @EJB
  EncryptionLocalBusiness encryptionBusiness;

  /**
   * Get method to generate AES key.
   *
   * @return
   */
  @GET
  @Path("aes/key/128")
  @Produces(MediaType.APPLICATION_JSON)
  public AESKeyResponse newAESKey128() {
    AESKeyResponse response = new AESKeyResponse();

    try {
      SecretKey key = encryptionBusiness.generateAESKey(128);
      response.setKey(key.getEncoded());
      response.setStatus(BaseResult.Status.SUCCESS);
    } catch (Exception ex) {
      response.setMessage(ex.getMessage());
      response.setStatus(BaseResult.Status.FAILURE);
    }

    return response;
  }

  @GET
  @Path("aes/key/192")
  @Produces(MediaType.APPLICATION_JSON)
  public AESKeyResponse newAESKey192() {
    AESKeyResponse response = new AESKeyResponse();

    try {
      SecretKey key = encryptionBusiness.generateAESKey(192);
      response.setKey(key.getEncoded());
      response.setStatus(BaseResult.Status.SUCCESS);
    } catch (Exception ex) {
      response.setMessage(ex.getMessage());
      response.setStatus(BaseResult.Status.FAILURE);
    }

    return response;
  }

  @GET
  @Path("aes/key/256")
  @Produces(MediaType.APPLICATION_JSON)
  public AESKeyResponse newAESKey256() {
    AESKeyResponse response = new AESKeyResponse();

    try {
      SecretKey key = encryptionBusiness.generateAESKey(256);
      response.setKey(key.getEncoded());
      response.setStatus(BaseResult.Status.SUCCESS);

    } catch (Exception ex) {
      response.setMessage(ex.getMessage());
      response.setStatus(BaseResult.Status.FAILURE);
    }

    return response;
  }

  @GET
  @Path("rsa/key/1024")
  @Produces(MediaType.APPLICATION_JSON)
  public RSAKeyResponse newRSAKey1024() {
    RSAKeyResponse response = new RSAKeyResponse();

    try {
      KeyPair keyPair = encryptionBusiness.generateRSAKeyPair(1024);
      response.setPrivateKey(keyPair.getPrivate().getEncoded());
      response.setPublicKey(keyPair.getPublic().getEncoded());
      response.setStatus(BaseResult.Status.SUCCESS);
    } catch (Exception ex) {
      response.setMessage(ex.getMessage());
      response.setStatus(BaseResult.Status.FAILURE);
    }

    return response;
  }

  @GET
  @Path("rsa/key/2048")
  @Produces(MediaType.APPLICATION_JSON)
  public RSAKeyResponse newRSAKey2048() {
    RSAKeyResponse response = new RSAKeyResponse();

    try {
      KeyPair keyPair = encryptionBusiness.generateRSAKeyPair(2048);
      response.setPrivateKey(keyPair.getPrivate().getEncoded());
      response.setPublicKey(keyPair.getPublic().getEncoded());
      response.setStatus(BaseResult.Status.SUCCESS);
    } catch (Exception ex) {
      response.setMessage(ex.getMessage());
      response.setStatus(BaseResult.Status.FAILURE);
    }

    return response;
  }

  @POST
  @Path("aes/encrypt")
  @Consumes(MediaType.APPLICATION_JSON)
  @Produces(MediaType.APPLICATION_JSON)
  public AESResponse aesEncrypt(final AESRequest request) {
    AESResponse response = new AESResponse();

    try {
      byte[] encrypted = encryptionBusiness.encryptAES(request.getKey(), request.getData());
      response.setData(encrypted);
      response.setStatus(BaseResult.Status.SUCCESS);
    } catch (Exception ex) {
      response.setMessage(ex.getMessage());
      response.setStatus(BaseResult.Status.FAILURE);
    }

    return response;
  }

  @POST
  @Path("aes/decrypt")
  @Consumes(MediaType.APPLICATION_JSON)
  @Produces(MediaType.APPLICATION_JSON)
  public AESResponse aesDecrypt(final AESRequest request) {
    AESResponse response = new AESResponse();

    try {
      byte[] decrypted = encryptionBusiness.decryptAES(request.getKey(), request.getData());
      response.setData(decrypted);
      response.setStatus(BaseResult.Status.SUCCESS);
    } catch (Exception ex) {
      response.setMessage(ex.getMessage());
      response.setStatus(BaseResult.Status.FAILURE);
    }

    return response;
  }

  @POST
  @Path("rsa/encrypt")
  @Consumes(MediaType.APPLICATION_JSON)
  @Produces(MediaType.APPLICATION_JSON)
  public RSAResponse rsaEncrypt(final RSARequest request) {
    RSAResponse response = new RSAResponse();

    try {
      PublicKey publicKey = encryptionBusiness.createRSAPublicKey(request.getKey());
      int keySize = encryptionBusiness.getRSAKeySize((RSAKey) publicKey);
      int blockSize = keySize / 8 - 11;

      if (request.getData().length > blockSize) {
        byte[] encrypted = encryptionBusiness.encryptRSABlock(publicKey, request.getData(), keySize);
        response.setData(encrypted);
        response.setStatus(BaseResult.Status.SUCCESS);
      } else {
        byte[] encrypted = encryptionBusiness.encryptRSA(publicKey, request.getData());
        response.setData(encrypted);
        response.setStatus(BaseResult.Status.SUCCESS);
      }
    } catch (Exception ex) {
      response.setMessage(ex.getMessage());
      response.setStatus(BaseResult.Status.FAILURE);
    }

    return response;
  }

  @POST
  @Path("rsa/decrypt")
  @Consumes(MediaType.APPLICATION_JSON)
  @Produces(MediaType.APPLICATION_JSON)
  public RSAResponse rsaDecrypt(final RSARequest request) {
    RSAResponse response = new RSAResponse();

    try {
      PrivateKey privateKey = encryptionBusiness.createRSAPrivateKey(request.getKey());
      int keySize = encryptionBusiness.getRSAKeySize((RSAKey) privateKey);
      int blockSize = keySize / 8;

      if (request.getData().length > blockSize) {
        byte[] decrypted = encryptionBusiness.decryptRSABlock(privateKey, request.getData(), keySize);
        response.setData(decrypted);
        response.setStatus(BaseResult.Status.SUCCESS);
      } else {
        byte[] decrypted = encryptionBusiness.decryptRSA(privateKey, request.getData());
        response.setData(decrypted);
        response.setStatus(BaseResult.Status.SUCCESS);
      }
    } catch (Exception ex) {
      response.setMessage(ex.getMessage());
      response.setStatus(BaseResult.Status.FAILURE);
    }

    return response;
  }

  @GET
  @Path("blowfish/key/128")
  @Produces(MediaType.APPLICATION_JSON)
  public AESKeyResponse newBlowfishKey128() {
    AESKeyResponse response = new AESKeyResponse();

    try {
      SecretKey key = encryptionBusiness.generateBlowfishKey(128);
      response.setKey(key.getEncoded());
      response.setStatus(BaseResult.Status.SUCCESS);
    } catch (Exception ex) {
      response.setMessage(ex.getMessage());
      response.setStatus(BaseResult.Status.FAILURE);
    }

    return response;
  }

  @GET
  @Path("blowfish/key/192")
  @Produces(MediaType.APPLICATION_JSON)
  public AESKeyResponse newBlowfishKey192() {
    AESKeyResponse response = new AESKeyResponse();

    try {
      SecretKey key = encryptionBusiness.generateBlowfishKey(192);
      response.setKey(key.getEncoded());
      response.setStatus(BaseResult.Status.SUCCESS);
    } catch (Exception ex) {
      response.setMessage(ex.getMessage());
      response.setStatus(BaseResult.Status.FAILURE);
    }

    return response;
  }

  @GET
  @Path("blowfish/key/256")
  @Produces(MediaType.APPLICATION_JSON)
  public AESKeyResponse newBlowfishKey256() {
    AESKeyResponse response = new AESKeyResponse();

    try {
      SecretKey key = encryptionBusiness.generateBlowfishKey(256);
      response.setKey(key.getEncoded());
      response.setStatus(BaseResult.Status.SUCCESS);
    } catch (Exception ex) {
      response.setMessage(ex.getMessage());
      response.setStatus(BaseResult.Status.FAILURE);
    }

    return response;
  }

  @POST
  @Path("blowfish/encrypt")
  @Consumes(MediaType.APPLICATION_JSON)
  @Produces(MediaType.APPLICATION_JSON)
  public AESResponse blowfishEncrypt(final AESRequest request) {
    AESResponse response = new AESResponse();

    try {
      byte[] encrypted = encryptionBusiness.encryptBlowfish(request.getKey(), request.getData());
      response.setData(encrypted);
      response.setStatus(BaseResult.Status.SUCCESS);
    } catch (Exception ex) {
      response.setMessage(ex.getMessage());
      response.setStatus(BaseResult.Status.FAILURE);
    }

    return response;
  }

  @POST
  @Path("blowfish/decrypt")
  @Consumes(MediaType.APPLICATION_JSON)
  @Produces(MediaType.APPLICATION_JSON)
  public AESResponse blowfishDecrypt(final AESRequest request) {
    AESResponse response = new AESResponse();

    try {
      byte[] decrypted = encryptionBusiness.decryptBlowfish(request.getKey(), request.getData());
      response.setData(decrypted);
      response.setStatus(BaseResult.Status.SUCCESS);
    } catch (Exception ex) {
      response.setMessage(ex.getMessage());
      response.setStatus(BaseResult.Status.FAILURE);
    }

    return response;
  }
}